home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 January: Mac OS SDK / Dev.CD Jan 00 SDK1.toast / Development Kits / Mac OS / Multiprocessing 2.0 SDK / Sample Code / HappyTrails ƒ / HappyTrails.c next >
Encoding:
C/C++ Source or Header  |  1999-08-03  |  16.2 KB  |  715 lines  |  [TEXT/CWIE]

  1. /**\
  2. |**|    HappyTrails.c
  3. \**/
  4.  
  5. #include <stdio.h>
  6. #include <stdarg.h>
  7.  
  8. #include "HappyTrails.h"
  9.  
  10. // typdef's, struct's, define's, enum's, etc.
  11.  
  12. #define kMPStackSize 0        // use default stack size
  13. #define kMPTaskOptions 0    // use no options
  14.  
  15. enum
  16. {
  17.     mAppleMenu = 128,
  18.         iAboutBox = 1,
  19.     mFileMenu = 129,
  20.         iQuit = 1,
  21.     mTasksMenu = 130,
  22.     mWeightMenu = 131
  23. };
  24.  
  25. typedef struct BallStruct {
  26.     Point        fPosition;
  27.     Point        fDelta;
  28.     RGBColor    fColor;
  29.     UInt32        fLength;
  30. } BallRec,*BallPtr,**BallHdl;
  31.  
  32. typedef struct MyTaskStruct {
  33.     MPTaskID        fTaskID;
  34.     MPTaskWeight    fWeight;
  35.     Rect            fBounds;
  36.     UInt32            fIndex;
  37.     UInt32            fBallNum;
  38.     BallPtr         fBallPtr;
  39. }MyTaskRec,*MyTaskPtr,**MyTaskHdl;
  40.  
  41. // external globals
  42.  
  43. UInt32        gNumTasks = 0;
  44.  
  45. // local (static) globals
  46.  
  47. static PixMapHandle gWindowPixMap = nil;
  48. static UInt8*         gWindowBasePtr = nil;
  49.  
  50. static GWorldPtr    gOffscreenPtr = nil;
  51. static Rect            gOffscreenRect = {0,0,kBallHeight,kBallWidth};
  52. static PixMapHandle gOffscreenPixMap = nil;
  53. static UInt8*         gOffscreenBasePtr = nil;
  54.  
  55. static Boolean gPaused = false;
  56. static Boolean gLoop = true;
  57.  
  58. static MPQueueID gNotificationQueueID;
  59. static MyTaskPtr gMyTaskPtrs = nil;
  60.  
  61. static MenuHandle gContextualMenuHdl = nil;
  62.  
  63. // local function prototypes
  64.  
  65. static OSStatus NewBall(MyTaskPtr pMyTaskPtr);
  66.  
  67. static OSStatus MyTask(void* pMyTaskPtr);
  68.  
  69. static void Draw_Ball(const Rect* pRectPtr,const RGBColor* pBallColorPtr);
  70. static void BlitBall(const Rect *pBallRect,const RGBColor* pBallColorPtr);
  71. static Boolean rects_Intersect(const Rect* pRect1,const Rect* pRect2);
  72. static void offset_rect (Rect *r,const short dh,const short dv);
  73. static SInt16 random();
  74.  
  75. #define ABS(x) ((x) < 0 ? -(x) : (x))
  76.  
  77. // local functions
  78.  
  79. /**\
  80. |**|    printf to debugger
  81. \**/
  82.  
  83. static int dprintf(const char *format,...)
  84. {
  85.     char buffer[257]; /* [length byte] + [255 string bytes] + [null] */
  86.     va_list arglist;
  87.     int return_value = 0;
  88.     
  89.     va_start(arglist, format);
  90.     return_value = vsprintf(buffer, format, arglist);
  91.     va_end(arglist);
  92.  
  93.     debugstr(buffer);    
  94.  
  95.     return return_value;
  96. }
  97.  
  98.  
  99. /**\
  100. |**|    convert index (1 <-> gNumTasks) to weight
  101. \**/
  102.  
  103. static MPTaskWeight TaskToWeight(UInt32 pTaskIndex)
  104. {
  105.     float_t e = exp(1); // 2.718281828;
  106.     if (gNumTasks <= 1)
  107.         return 100;
  108.     else
  109.         return (50 * (pow(e,(log(200) * pTaskIndex) / (gNumTasks - 1))));
  110. }
  111.  
  112. /**\
  113. |**|    Initialize everything for HappyTrails, make sure we can run
  114. \**/
  115.  
  116. OSStatus HT_Init(UInt32 pNumTasks)
  117. {
  118.     OSStatus    error;
  119.     UInt32        index;
  120.  
  121.     if (!MPLibraryIsLoaded())
  122.         return kMPInsufficientResourcesErr;
  123.  
  124.     gLoop = true;
  125.  
  126.     gWindowPixMap = ((CWindowPtr) gWindowPtr)->portPixMap;
  127.     if (!LockPixels(gWindowPixMap))
  128.         DebugStr("\p|HT_Init-F-LockPixels(gWindowPixMap) error.;es");
  129.  
  130.     gWindowBasePtr = (UInt8*) GetPixBaseAddr(gWindowPixMap);
  131.  
  132.     {
  133.         RGBColor whiteRGBColor = {-1,-1,-1};
  134.         CGrafPtr savePort;
  135.         GDHandle saveGDHdl;
  136.  
  137.         GetGWorld(&savePort, &saveGDHdl);
  138.  
  139.         // Create an offscreen GWorld and draw our SillyBall into it.
  140.         // The MP tasks will copy from this GWorlds PixMap to our
  141.         // windows PixMap changing the color on-the-fly.
  142.  
  143.         error = NewGWorld(&gOffscreenPtr, 1, &gOffscreenRect, nil, nil, keepLocal);
  144.         if (error != noErr)
  145.             dprintf("|HT_Init-F-NewGWorld error: %d.;es",error);
  146.  
  147.         gOffscreenPixMap = GetGWorldPixMap(gOffscreenPtr);
  148.         if (!LockPixels(gOffscreenPixMap))
  149.             DebugStr("\p|HT_Init-F-LockPixels(gOffscreenPixMap) error.;es");
  150.  
  151.         gOffscreenBasePtr = (UInt8*) GetPixBaseAddr(gOffscreenPixMap);
  152.  
  153.         SetGWorld(gOffscreenPtr, nil);
  154.         EraseRect(&gOffscreenRect);
  155.         Draw_Ball(&gOffscreenRect,&whiteRGBColor);
  156.  
  157.         SetGWorld(savePort, saveGDHdl);
  158.     }
  159.  
  160.     // Initialize remaining globals
  161.     gNotificationQueueID = NULL;
  162.     gNumTasks = pNumTasks;
  163.  
  164.     // create the notification queue            
  165.     error = MPCreateQueue(&gNotificationQueueID);
  166.     if (error != noErr)
  167.         return error;
  168.  
  169.     // allocate the task records
  170.     gMyTaskPtrs = (MyTaskPtr) NewPtrClear(sizeof(MyTaskRec) * gNumTasks);
  171.     if (!gMyTaskPtrs)
  172.     {
  173.         error = MemError();
  174.         if (error == noErr)
  175.             error = memFullErr;
  176.         return error;
  177.     }
  178.  
  179.     {
  180.         UInt16 width = gWindowRect.right - gWindowRect.left;
  181.         UInt16 height = gWindowRect.bottom - gWindowRect.top;
  182.         UInt16 rows,cols;
  183.  
  184.         cols = sqrt(gNumTasks * 4 / 3);
  185.         rows = gNumTasks / cols;
  186.  
  187.         if ((rows * cols) < gNumTasks)
  188.             rows++;
  189.  
  190.         width /= cols;
  191.         height /= rows;
  192.  
  193.         EraseRect(&gWindowRect);
  194.  
  195.         for (index = 0; (index < gNumTasks) && (error == noErr); index++)
  196.         {
  197.             MyTaskPtr tMyTaskPtr = &gMyTaskPtrs[index];
  198.             Rect tRect;
  199.  
  200.             tRect.bottom = (tRect.top = height * (index / cols)) + height;
  201.             tRect.right = (tRect.left = width * (index % cols)) + width;
  202.  
  203.             FrameRect(&tRect);
  204.  
  205.             tMyTaskPtr->fBounds = tRect;
  206.             tMyTaskPtr->fWeight = TaskToWeight(index); // 16 * (1 << index);
  207.             tMyTaskPtr->fIndex = index;
  208.  
  209.             tMyTaskPtr->fBallPtr = (BallPtr) NewPtrClear(sizeof(BallRec) * kNumBalls);
  210.  
  211. #    if !SMP_TEST
  212.             error = MPCreateTask(MyTask, tMyTaskPtr,    // task entry point & parameter
  213.                 kMPStackSize, gNotificationQueueID,        // stack size & notification queue ID
  214.                 tMyTaskPtr, NULL, kMPTaskOptions,        // Notify params 1 & 2 and task options
  215.                 &tMyTaskPtr->fTaskID);                    // task id
  216.             if (error != noErr)
  217.             {
  218.                 dprintf("|HT_Init-F-MPCreateTask error: %d.;g",error);
  219.                 return error;
  220.             }
  221.  
  222.             error = MPSetTaskWeight(tMyTaskPtr->fTaskID,tMyTaskPtr->fWeight);
  223.             if (error != noErr)
  224.             {
  225.                 dprintf("|HT_Init-F-MPSetTaskWeight error: %d.;g",error);
  226.                 return error;
  227.             }
  228. #    endif !SMP_TEST
  229.         }
  230.     }
  231.  
  232.     gContextualMenuHdl = GetMenu(mWeightMenu);
  233.     InsertMenu(gContextualMenuHdl, -1);
  234.  
  235.     // If something went wrong, just go back to single processor mode
  236.     if (error != noErr)
  237.         gNumTasks = 1;
  238.  
  239.     return error;
  240. }    // HT_Init
  241.  
  242. /**\
  243. |**|    Cleanup everything
  244. \**/
  245.  
  246. void HT_Term(void)
  247. {
  248.     OSStatus error;
  249.     UInt32    index;
  250.  
  251.     gInBackGround = gLoop = false;
  252.  
  253.     if (gMyTaskPtrs)
  254.     {
  255.         for (index = 0; index < gNumTasks; index++)
  256.         {
  257.             MyTaskPtr tMyTaskPtr = &gMyTaskPtrs[index];
  258.             if (tMyTaskPtr->fTaskID != NULL)
  259.             {
  260.                 // terminate this task
  261.                 error = MPTerminateTask(tMyTaskPtr->fTaskID, kMPTaskAbortedErr);
  262.                 if (error != noErr)
  263.                     dprintf("|HT_Term-F-MPTerminateTask error: %d.;g",error);
  264.  
  265.                 // Now wait for notification that this task has terminated
  266.                 while ((error = MPWaitOnQueue(gNotificationQueueID,
  267.                     (void*) &tMyTaskPtr, NULL, NULL, kDurationImmediate)) == noErr)
  268.                 {
  269.                     if (tMyTaskPtr->fTaskID != NULL)
  270.                     {
  271.                         tMyTaskPtr->fTaskID = NULL;
  272.  
  273.                         if (tMyTaskPtr->fBallPtr)
  274.                             DisposePtr((Ptr) tMyTaskPtr->fBallPtr);
  275.                         tMyTaskPtr->fBallPtr = nil;
  276.                     }
  277.                 }
  278.             }
  279.         }
  280.  
  281.         // delete the notification queue
  282.         if (gNotificationQueueID != NULL)
  283.         {
  284.             MPDeleteQueue(gNotificationQueueID);
  285.             gNotificationQueueID = NULL;
  286.         }
  287.  
  288.         // delete the task data
  289.         DisposePtr((Ptr)gMyTaskPtrs);
  290.         gMyTaskPtrs = NULL;
  291.     }
  292.  
  293.     if (gOffscreenPtr)
  294.         DisposeGWorld(gOffscreenPtr);
  295.     gOffscreenPtr = nil;
  296. }
  297.  
  298. /**\
  299. |**|    Handle a null event
  300. \**/
  301.  
  302. void HT_DoNull(void)
  303. {
  304. # if SMP_TEST
  305.     UInt32 index;
  306.  
  307.     gLoop = false;
  308.     for (index = 0; index < gNumTasks;index++)
  309.         MyTask((void*) &gMyTaskPtrs[index]);
  310. # else
  311. //    MPYield();
  312. # endif SMP_TEST
  313. }
  314.  
  315. /**\
  316. |**|    Handle a mouse down event
  317. \**/
  318.  
  319. void HT_DoClick(const EventRecord *pEventPtr)
  320. {
  321.     MyTaskPtr tMyTaskPtr = nil;
  322.     Point where = pEventPtr->where;
  323.     UInt32 index;
  324.  
  325.     GlobalToLocal(&where);
  326.  
  327.     for (index = 0;index < gNumTasks;index++)
  328.     {
  329.         if (PtInRect(where,&gMyTaskPtrs[index].fBounds))
  330.             tMyTaskPtr = &gMyTaskPtrs[index];
  331.     }
  332.  
  333.     if (!tMyTaskPtr || !gContextualMenuHdl)
  334.         goto beep;
  335.  
  336.     {
  337.         SInt32 weight = (SInt32) tMyTaskPtr->fWeight;
  338.         SInt16 count = CountMItems(gContextualMenuHdl);
  339.         SInt32 value;
  340.         Boolean first = true;
  341.  
  342.         for (index = 1;index <= count;index++)
  343.         {
  344.             Str255 tStr255;
  345.             UInt32 temp = gNumTasks;
  346.  
  347.             gNumTasks = count;
  348.             value = TaskToWeight(index - 1);
  349.             gNumTasks = temp;
  350.  
  351.             NumToString(value, tStr255);
  352.             SetMenuItemText(gContextualMenuHdl, index, tStr255);
  353.             EnableItem(gContextualMenuHdl, index);
  354.  
  355.             if (value < weight)
  356.                 CheckItem(gContextualMenuHdl, index, false);
  357.             else
  358.             {
  359.                 CheckItem(gContextualMenuHdl, index, first);
  360.                 if (first)
  361.                     weight = value;
  362.                 first = false;
  363.             }
  364.         }
  365.  
  366.         if (IsShowContextualMenuClick(pEventPtr))
  367.         {
  368.             UInt32 outUserSelectionType;
  369.             SInt16 outMenuID;
  370.             UInt16 outMenuItem;
  371.             OSStatus tOSStatus;
  372.  
  373.             gPaused = true;
  374.             tOSStatus = ContextualMenuSelect(gContextualMenuHdl,
  375.                                             pEventPtr->where,
  376.                                             false,
  377.                                             nil,
  378.                                             nil,
  379.                                             nil,
  380.                                             &outUserSelectionType,
  381.                                             &outMenuID,
  382.                                             &outMenuItem);
  383.             gPaused = false;
  384.             if ((tOSStatus == noErr) && (outUserSelectionType == kCMMenuItemSelected))
  385.             {
  386.                 if (outMenuID == mWeightMenu)
  387.                 {
  388.                     Str255 tStr255;
  389.                     OSStatus error;
  390.  
  391.                     GetMenuItemText(gContextualMenuHdl, outMenuItem, tStr255);
  392.                     StringToNum(tStr255, &value);
  393.                     tMyTaskPtr->fWeight = value;
  394.  
  395.                     index = (long) tMyTaskPtr->fIndex;
  396.  
  397.                     error = MPSetTaskWeight(tMyTaskPtr->fTaskID, value);
  398.                     if (error != noErr)
  399.                         dprintf("|HT_DoClick-F-MPSetTaskWeight error: %d.;g",error);
  400.                 }
  401.             }
  402.             return;
  403.         }
  404.         else
  405.             goto beep;
  406.     }
  407. beep:
  408.         SysBeep(15);
  409. }
  410.  
  411. /**\
  412. |**|    This is the MP task entrypoint
  413. \**/
  414.  
  415. OSStatus MyTask(void* pMyTaskPtr)
  416. {
  417.     UInt32 index;
  418.     for (index = 0;index < gNumTasks;index++)
  419.     {
  420.         if ((long) pMyTaskPtr == (long) &gMyTaskPtrs[index])
  421.             break;
  422.     }
  423.     if (index >= gNumTasks)
  424.         return noErr;
  425.  
  426.     do {
  427.         NewBall((MyTaskPtr) pMyTaskPtr);
  428.         do {} while (gInBackGround);
  429.     } while (gLoop);
  430.     return noErr;
  431. }
  432.  
  433. /**\
  434. |**|    Draw the next ball
  435. \**/
  436.  
  437. static OSStatus NewBall(MyTaskPtr pMyTaskPtr)
  438. {
  439.     Rect        ballRect;
  440.     Rect        bounds = pMyTaskPtr->fBounds;
  441.     UInt32        newLeft,newTop;
  442.     UInt32        width = bounds.right - bounds.left;
  443.     UInt32        height = bounds.bottom - bounds.top;
  444.     RGBColor    ballColor;
  445.  
  446.     BallPtr        tBallPtr;
  447.  
  448.     if (pMyTaskPtr->fBallNum >= kNumBalls)
  449.         pMyTaskPtr->fBallNum = 0;
  450.  
  451.     if (gPaused)
  452.         return noErr;
  453.  
  454.     tBallPtr = &pMyTaskPtr->fBallPtr[pMyTaskPtr->fBallNum++];
  455.  
  456.     if (tBallPtr->fLength)
  457.     {
  458.         newTop = tBallPtr->fPosition.v;
  459.         newLeft = tBallPtr->fPosition.h;
  460.  
  461.         ballRect.bottom = (ballRect.top = newTop) + kBallHeight;
  462.         ballRect.right = (ballRect.left = newLeft) + kBallWidth;
  463.  
  464.         ballColor = tBallPtr->fColor;
  465.         if (tBallPtr->fLength == 1)
  466.         {
  467.             ballColor.red ^= 0x8000;
  468.             ballColor.green ^= 0x8000;
  469.             ballColor.blue ^= 0x8000;
  470.         }
  471.     }
  472.     else
  473.     {
  474.         // 
  475.         //    Make a random new color for the ball.
  476.         //
  477.         ballColor.red   = (unsigned short) random();
  478.         ballColor.green = (unsigned short) random();
  479.         ballColor.blue  = (unsigned short) random();
  480.         tBallPtr->fColor = ballColor;
  481.  
  482.         //    
  483.         //    Make a random new location for the ball, that is normalized to the window size.  
  484.         //    This makes the Integer from random into a number that is 0..bounds.bottom
  485.         //    and 0..bounds.right.  They are normalized so that we don't spend most of our
  486.         //    time drawing in places outside of the window.
  487.         //
  488.         do
  489.         {
  490.             newTop = 32767 + random();    newLeft = 32767 + random();
  491.             newTop = bounds.top + ((newTop * (height - kBallHeight)) / 65536);
  492.             newLeft = bounds.left + ((newLeft * (width - kBallWidth)) / 65536);
  493.  
  494.             ballRect.bottom = (ballRect.top = newTop) + kBallHeight;
  495.             ballRect.right = (ballRect.left = newLeft) + kBallWidth;
  496.  
  497.         } while (    (ballRect.top < bounds.top) ||
  498.                     (ballRect.left < bounds.left) ||
  499.                     (ballRect.bottom >= bounds.bottom) ||
  500.                     (ballRect.right >= bounds.right)
  501.                 );
  502.  
  503.         // 
  504.         //    Make a random new delta direction
  505.         //
  506.  
  507.         do {
  508.             tBallPtr->fDelta.h = random() % 5;
  509.             tBallPtr->fDelta.v = random() % 5;
  510.         } while ((tBallPtr->fDelta.h == 0) || (tBallPtr->fDelta.v == 0));
  511.  
  512.         tBallPtr->fLength = 0x3F & random();    // width + ABS(random() % height);
  513.     }
  514.  
  515.     if (!rects_Intersect(&ballRect,&gMenuRect))
  516.         BlitBall(&ballRect,&ballColor);
  517.  
  518.     if (tBallPtr->fLength)
  519.         tBallPtr->fLength--;
  520.  
  521.     offset_rect(&ballRect,tBallPtr->fDelta.h,0);
  522.     if (ballRect.left <= bounds.left)
  523.     {
  524.         offset_rect(&ballRect, bounds.left - ballRect.left,0);
  525.         tBallPtr->fDelta.h = ABS(tBallPtr->fDelta.h);
  526.         do {tBallPtr->fDelta.v = (tBallPtr->fDelta.v + (random() % 2)) % 5;} while (!tBallPtr->fDelta.v);
  527.     }
  528.     else if (ballRect.right >= bounds.right)
  529.     {
  530.         offset_rect(&ballRect, bounds.right - ballRect.right,0);
  531.         tBallPtr->fDelta.h = -ABS(tBallPtr->fDelta.h);
  532.         do {tBallPtr->fDelta.v = (tBallPtr->fDelta.v + (random() % 2)) % 5;} while (!tBallPtr->fDelta.v);
  533.     }
  534.  
  535.     offset_rect(&ballRect,0,tBallPtr->fDelta.v);
  536.     if (ballRect.top <= bounds.top)
  537.     {
  538.         offset_rect(&ballRect,0, bounds.top - ballRect.top);
  539.         tBallPtr->fDelta.v = ABS(tBallPtr->fDelta.v);
  540.         do {tBallPtr->fDelta.h = (tBallPtr->fDelta.h + (random() % 2)) % 5;} while (!tBallPtr->fDelta.h);
  541.     }
  542.     else if (ballRect.bottom >= bounds.bottom)
  543.     {
  544.         offset_rect(&ballRect,0, bounds.bottom - ballRect.bottom);
  545.         tBallPtr->fDelta.v = -ABS(tBallPtr->fDelta.v);
  546.         do {tBallPtr->fDelta.h = (tBallPtr->fDelta.h + (random() % 2)) % 5;} while (!tBallPtr->fDelta.h);
  547.     }
  548.     tBallPtr->fPosition = *(Point*) &ballRect;
  549.  
  550.     return noErr;
  551. }
  552.  
  553. /**\
  554. |**|    draw a ball
  555. \**/
  556.  
  557. static void Draw_Ball(const Rect* pRectPtr,const RGBColor* pBallColorPtr)
  558. {
  559.     RGBColor invertRGBColor = *pBallColorPtr;
  560.     // 
  561.     //    Set that color as the new color to use in drawing.
  562.     //
  563.     RGBForeColor(pBallColorPtr);
  564.  
  565.     //
  566.     //    Move pen to the new location, and paint the colored ball.
  567.     //
  568.     PaintOval (pRectPtr);
  569.     
  570.     //
  571.     //    Move the pen to the middle of the new ball position, for the text
  572.     //
  573.     MoveTo((short)(pRectPtr->left + kBallWidth/2 - kTextSize), 
  574.         (short)(pRectPtr->top + kBallHeight/2 + kTextSize/2 -1));
  575.     
  576.     //    
  577.     //    Invert the color and draw the text there.  This won’t look quite right in 1 bit
  578.     //    mode, since the foreground and background colors will be the same.
  579.     //    Color QuickDraw special cases this to not invert the color, to avoid
  580.     //    invisible drawing.
  581.     //
  582.     InvertColor(&invertRGBColor); 
  583.     RGBForeColor(&invertRGBColor);
  584.  
  585.     // (
  586.     DrawString("\p;-)");
  587.  
  588.     InvertOval(&gOffscreenRect);
  589. }
  590.  
  591. /**\
  592. |**|    Blit a ball onscreen
  593. \**/
  594.  
  595. static void BlitBall(const Rect *pBallRect,const RGBColor* pBallColorPtr)
  596. {
  597.     UInt16 srcSkip = (*gOffscreenPixMap)->rowBytes & 0x3FFF;
  598.     UInt16 dstSkip = (*gWindowPixMap)->rowBytes & 0x3FFF;
  599.  
  600.     UInt8 *srcBase = gOffscreenBasePtr +
  601.         (srcSkip * (gOffscreenRect.top - (*gOffscreenPixMap)->bounds.top)) +
  602.         ((gOffscreenRect.left - (*gOffscreenPixMap)->bounds.left) >> 3);
  603.  
  604.     UInt8 *dstBase = gWindowBasePtr +
  605.         (dstSkip * (pBallRect->top - (*gWindowPixMap)->bounds.top));
  606.  
  607.     UInt16 r,c,pixelSize = (*gWindowPixMap)->pixelSize;
  608.  
  609.     UInt32 color32 = 0x00FFFFFF & (
  610.         (pBallColorPtr->red << 16) ^
  611.         (pBallColorPtr->green << 8) ^
  612.         (pBallColorPtr->blue << 0)
  613.     );
  614.     UInt16 color16 = 0x7FFF & color32;
  615.     UInt8 color8 = 0xFF & color16;
  616.  
  617.     switch (pixelSize)
  618.     {
  619.         case 1:        // b/w
  620.         case 2:        // 4 colors
  621.         case 4:        // 16 colors
  622.             break;    // Sorry, Homey don't play that! (Yet!)
  623.         case 8:        // 256 colors
  624.             dstBase += pBallRect->left - (*gWindowPixMap)->bounds.left;
  625.             break;
  626.         case 16:    // thousand colors
  627.             dstBase += (pBallRect->left - (*gWindowPixMap)->bounds.left) << 1;
  628.             break;
  629.         case 32:    // million colors
  630.             dstBase += (pBallRect->left - (*gWindowPixMap)->bounds.left) << 2;
  631.             break;
  632.     }
  633.  
  634.     for (r = gOffscreenRect.top;r < gOffscreenRect.bottom;r++)
  635.     {
  636.         UInt8 *srcPtr = srcBase,*dstPtr = dstBase;
  637.         UInt8 data = *srcPtr++;
  638.         for (c = gOffscreenRect.left;c < gOffscreenRect.right;c++)
  639.         {
  640.             UInt8 i = 7 - (c % 8);
  641.  
  642.             switch (pixelSize)
  643.             {
  644.                 case 1:        // b/w
  645.                 case 2:        // 4 colors
  646.                 case 4:        // 16 colors
  647.                     break;    // Sorry, Homey don't play that! (Yet!)
  648.                 case 8:        // 256 colors
  649.                     if ((data >> i) & 1)    // source bit is set
  650.                         *dstPtr = color8;
  651.                     dstPtr++;
  652.                     break;
  653.                 case 16:    // b/w depth
  654.                     if ((data >> i) & 1)    // source bit is set
  655.                         *(UInt16*)dstPtr = color16;
  656.                     dstPtr += 2;
  657.                     break;
  658.                 case 32:    // b/w depth
  659.                     if ((data >> i) & 1)    // source bit is set
  660.                         *(UInt32*)dstPtr = color32;
  661.                     dstPtr += 4;
  662.                     break;
  663.             }
  664.  
  665.             if (i == 0)
  666.                 data = *srcPtr++;
  667.         }
  668.         srcBase += srcSkip;
  669.         dstBase += dstSkip;
  670.     }
  671. }
  672.  
  673. /**\
  674. |**|    Test to see if rectangles intersect
  675. \**/
  676.  
  677. static Boolean rects_Intersect(const Rect* pRect1,const Rect* pRect2)
  678. {
  679.     if (pRect1->bottom <= pRect2->top)
  680.         return false;
  681.     if (pRect1->top >= pRect2->bottom)
  682.         return false;
  683.     if (pRect1->right <= pRect2->left)
  684.         return false;
  685.     if (pRect1->left >= pRect2->right)
  686.         return false;
  687.     return true;
  688. }
  689.  
  690. /**\
  691. |**|    move a rectangle
  692. \**/
  693.  
  694. static void offset_rect(Rect *r,const short dh,const short dv)
  695. {
  696.     r->top += dv;
  697.     r->left += dh;
  698.     r->bottom += dv;
  699.     r->right += dh;
  700. }
  701.  
  702. /**\
  703. |**|    generate a random number +/- 32768
  704. \**/
  705.  
  706. static SInt16 random()
  707. {
  708.     static SInt32 state = 314159;
  709.  
  710.     state = ((state * 1103515245) + 12345 ) & 0x7fffffff;
  711.  
  712.     return ((state >> 6) & 0xffff);
  713. }
  714.  
  715.